/*
 * To change this license header, choose License Headers in Project Properties.
 * To change this template file, choose Tools | Templates
 * and open the template in the editor.
 */

package JeuDeLaVie;

import java.awt.Point;
import java.io.File;
import java.util.HashMap;
import java.io.Serializable;


/**
 *
 * @author 
 */

public class Environnement extends Thread implements Serializable 
{
    static private final long serialVersionUID = 4L;

    private Case[][] tabCases;
    private transient HashMap mapCases;
    
    private transient EnvListener ecoutant;
    
    private transient MotifEnv[] tabMotifs;
    
    private transient boolean enPause;
    private transient boolean genereThreadsCases;

    private transient Regles[] tabRegles;
    
    //Création de la table de hachage des coordonnées
    //Utilisation de la clef de la case en tant que clé de la table
    /**
     *
     * @param mapCase
     * @param clef
     * @param x
     * @param y
     */
    private void rempMapCase(HashMap mapCase, int clef, int x, int y)
    {
        Point point = new Point();
        point.setLocation(x, y);
        mapCase.put(clef, point);
    }
        
    // Constructeur par défaut de la grille de cellules
    // Initialisation des Cases du tableau
    // (Case n'étant pas une structure primitive, il faut 
    // passer par son constructeur pour les tableaux aussi)
    public Environnement()
    {
        this(75); // appelle la version avec choix de taille, fixée à 65
        
    }
    
    //Constructeur secondaire de la grille de cellules
    public Environnement(int taille)
    {
        tabMotifs = new MotifEnv[3];
        tabMotifs[0] = new MotifPetitPlaneur();
        tabMotifs[1] = new MotifOscillCroix();
        tabMotifs[2] = new MotifPetitCanon();
        
        tabRegles = new Regles[3];
        tabRegles[0] = new ReglesBases();
        tabRegles[1] = new Regles1();
        tabRegles[2] = new ReglesBasePond();
        
        enPause = true;
	genereThreadsCases = true;

        tabCases = new Case[taille][taille];
        //Initialisation et remplissage de la table de hachage ici pour
        //une plus faible complexité
        HashMap<Integer, Point> mapCase;
        mapCase = new HashMap<>(taille);
        
        for (int i = 0; i < taille; i++)
	    {
		for (int j = 0; j < taille; j++)
		    {
			tabCases[i][j] = new Case();
			tabCases[i][j].setClef(i * (tabCases.length) + j);
			rempMapCase(mapCase,tabCases[i][j].getClef(),i,j);
		    }
	    }
        //Test si la taille du mappage de la table est égale au nombre de cases du tableau
        if (mapCase.size() != taille * taille)
        {
            System.out.println("La construcion de mapCases s'est mal passée");
        }
        
        // paramétrage des cases
        paramCasesVoisins();
//        grilleAleat();
        
        enPause = true;
	genereThreadsCases = true;
    }
    
    private void paramCasesVoisins(){
        for(int i=0; i<tabCases.length; i++){
            for(int j=0; j <tabCases.length; j++){
                
                int xVois, yVois;

                // voisins du haut
                for (int k = 0; k < 3; k++)
                {
                    // si i = 0, bordure supérieure, on prend la case du bas de tableau
                    if (i == 0)
                    {
                        xVois = tabCases.length - 1;
                    } else
                    {
                        xVois = i - 1;
                    }

                    if ((j == 0)&&(k == 0))
                    {
                        yVois = tabCases.length - 1;
                    } else if ((j == tabCases.length - 1)&&(k == 2))
                    {
                        yVois = 0;
                    } else
                    {
                        yVois = j+k-1;
                    }

                    
                    tabCases[i][j].setCaseVoisin(tabCases[xVois][yVois], k);

                }
                
                // voisin de gauche direct
                if(j == 0)
                    yVois = tabCases.length -1;
                else
                    yVois = j-1;
                tabCases[i][j].setCaseVoisin(tabCases[i][yVois], 3);
                
                // voisin de droite direct
                if(j == tabCases.length-1)
                    yVois = 0;
                else yVois = j+1;
                tabCases[i][j].setCaseVoisin(tabCases[i][yVois], 4);
                
                
                // voisins du dessous
                for (int k = 5; k < 8; k++)
                {
                    // si i = taille tableau -1, bordure inférieure, on prend la case du haut du tableau
                    if (i == tabCases.length-1)
                    {
                        xVois = 0;
                    } else
                    {
                        xVois = i + 1;
                    }

                    if ((j == 0)&&(k==5))
                    {
                        yVois = tabCases.length - 1;
                    } else if ((j == tabCases.length - 1)&& k==7)
                    {
                        yVois = 0;
                    } else
                    {
                        yVois = j+k-6;
                    }

                    tabCases[i][j].setCaseVoisin(tabCases[xVois][yVois], k);

                }
                
                
            }
        }
    }
    
    //===========
    // Thread principal
    //===========
    @Override
    public synchronized void run()
    {
	while(true)
	    {
		try{
		if(enPause == true)
		    wait();

//		    grilleAleat();
//                passageGrilleEtatSuivant();
                
                //============================================================================
                // test
                //=============================================================================
/*                Case caseTest = tabCases[1][1];
                Case[] tabTest = caseTest.getVoisinsCase();
                caseTest.calculEtatSuivant();
                
                for(int n=0; n<tabTest.length; n++){
                    System.out.println(tabTest[n].etatCourant + " | ");
                }
                    System.out.println("\n");
                    System.out.println("\nNombre de voisines vivantes : " + caseTest.tempVois + '\n');
                    
                    System.out.println("\nétat suivant : " + caseTest.etatSuivant + '\n');
*/                //=============================================================================
/*
                //test : on affiche le tableau
                int a;
                System.out.println("\n Contenu du tableau :\n\n");
                for(int t=0; t<tabCases.length; t++){
                    for(int u=0; u<tabCases.length; u++){
                        if(tabCases[t][u].etatCourant){
                            a = 1;
                        }
                        else{
                            a = 0;
                        }
                        System.out.print(a + " | ");
                    }
                    System.out.print("\n");
                }
  */              
                   sleep(5);
                   passageGrilleEtatSuivant();
		}
		catch(InterruptedException e)
		    {
			System.out.println("Erreur dans le thread de l'environnement");
		    }


	    }
    }

    public synchronized void endortThread()
    {
	try{
	    wait();
	}
	catch(InterruptedException e)
	    {
		System.out.println("Erreur dans le thread de l'environnement");
	    }
    }
    
    //============
    // Accesseurs
    //============
    
    public Case[][] getGrille(){
        return tabCases;
    }
    
    public MotifEnv getMotif(int num){
        try
	    {
		return tabMotifs[num];
	    } catch (Exception e)
	    {
		System.err.println("Ce motif n'existe pas");
		return null;
	    }
    }
    
    // test !!!!!
    public boolean getEnPause(){
        return enPause;
    }
    
    public Regles getRegles(int num){
        return tabRegles[num];
    }
    
    /*public int[] getVoisins(HashMap mapCase, Case case)
      {
      int x,y,xtmp,ytmp;
      x= mapCase.get
      for (int i = case.; i < taille; i++)
      {
      for (int j = 0; j < taille; j++)
      {
      mapCases.put(tabCases[i][j].getClef(), tabCases[i][j].getEtatCourant());
      }
      }
      }
    */
    
    //===========
    // Mutateurs
    //===========
    //idem
    public void setEcoutant(EnvListener envLis){
        ecoutant = envLis;
    }
    
    public void setReglesAct(int numRegles){
        for(int i=0; i<tabCases.length; i++){
            for(int j=0; j<tabCases.length; j++){
                tabCases[i][j].setRegles(tabRegles[numRegles]);
            }
        }
    }
    
    public void inverseEtatCase(Point coord){
        tabCases[coord.x][coord.y].inverseEtatCourant();
        chgmtCase(coord);
    }

    public void grilleAleat()
    {
        for (int i = 0; i < tabCases.length; i++)
	    {
		for (int j = 0; j < tabCases[i].length; j++)
		    {
			tabCases[i][j].setAleaEtat();
		    }
	    }
        
        // On prévient qu'on a bidouillé la grille
        chgmtGrilleEnv();
        
    }
    
    /**
     * Réinitialise le modèle : met toutes les cases à false
     * (on tue tout le monde !!!!!)
     */
    public void reInitGrilleEnv()
    {
        for (int i = 0; i < tabCases.length; i++)
	    {
		for (int j = 0; j < tabCases[i].length; j++)
		    {
			tabCases[i][j].initCase();
		    }
	    }
        // On prévient qu'on a bidouillé la grille
        chgmtGrilleEnv();
    }
    
    public void passageGrilleEtatSuivant()
    {
        int i;
		
        for (i = 0; i < tabCases.length; i++)
        {
            for (int j = 0; j < tabCases.length; j++)
            {
                tabCases[i][j].calculEtatSuivant();
            }
        }
	
	/*
	if(genereThreadsCases)
	    {
		for (i = 0; i < tabCases.length; i++)
		    for (int j = 0; j < tabCases[i].length; j++)
			{
			    synchronized(tabCases[i][j])
				{
				    tabCases[i][j].start();
				}
			}
	    }
	else notifyAll();
	*/
	genereThreadsCases = false;

        for (i = 0; i < tabCases.length; i++)
        {
            for (int j = 0; j < tabCases.length; j++)
            {
                tabCases[i][j].majEtatCourant();
            }
        }

        chgmtGrilleEnv();
    }
    
    //----------------------------------
    // Un truc pour dessiner des motifs
    //----------------------------------
    /**
     * Dessine dans le tableau de cases le motif demandé. Les bouts de motifs
     * situés hors tableau ne seront pas dessinés.
     * @param numMotif Le numéro du motif (du tableau de motifs de Environnement)
     * @param coord Les coordonnées de départ du tracé du Motif (le dessin commence en 0,0 du motif)
     */
    public void setMotif(int numMotif, Point coord){
        int xTab, yTab;
        Case[][] temp;
        temp = tabMotifs[numMotif].getTabMotif();
        
        for(int i=0; i<temp.length; i++){
            for(int j=0; j<temp[i].length; j++){
                xTab = coord.x + i;
                yTab = coord.y + j;
                
                if((xTab >= tabCases.length)||(yTab >= tabCases[i].length)){
                    // On ne fait rien
                    System.out.println("Hors tableau");
                }
                else{
                    System.out.println("Dans tableau : on le met");
                    tabCases[xTab][yTab].setEtatCourant(temp[i][j].getEtatCourant());
                }
            }
        }
        
        //On prévient qu'on a bidouillé la grille
        chgmtGrilleEnv();
    }
    
    //================================================
    // Fonctions avertissant qu'il se passe des trucs
    //================================================
    /* Fonction qui prévient que la grille a changé
     */
    public void chgmtGrilleEnv(){
        //La grille a changé, on prévient la fenêtre
        ecoutant.majGrille();
    }

    
    /**
     * Pour un changement immédiat d'une unique case :
     * fonction qui prévient que la case dont on donne
     * les coordonnées a changé.
     * @param coord Les coordonnées de la case qui a changé
     */
    public void chgmtCase(Point coord){
        ecoutant.majCase(coord);
    }
    
    
    
    //================================================================
    //Gestion de la mise en pause du threads
    //================================================================
    public void lancerSimu()
    {
	this.enPause = false;
    }

    public void pause()
    {
	this.enPause = true;
    }
    
    
        public void chargerSauvegarde()
    {
	//on regarde si il existe une sauvegarde de l'état précédent de l'Environnement et si oui on recupère ses etats
	File f = new File("environnement.serial");
	if (f.isFile() && f.canRead()) {
	    GestionFichier gestionFichier = new GestionFichier();
	    Environnement tmp = gestionFichier.lireFichier();

		for (int i = 0; i < tabCases.length; i++)
			for (int j = 0; j < tabCases[i].length; j++)
			    {
				tabCases[i][j].setEtatCourant(tmp.tabCases[i][j].getEtatCourant());
			    }  
	}
	// On affiche la nouvelle grille
        chgmtGrilleEnv();
    }

    
}
